from zope.interface import implements

from toneedu.decorators import paramNum, paramCheck, pluginRequired
from toneedu.interfaces import IParser
from toneedu.controller import controller
from toneedu.util import joinLines
from toneedu.exceptions import ToneEduError, NoSuchSoundDevice

class Parser(object):
    implements(IParser)
    
    def __init__(self):
        self._prompt_string = ">>> "

    def getPrompt(self):
        return self._prompt_string
    
    def issueCommand(self, command):
        tokens = command.lower().split()
        mainCmd = tokens[0] if len(tokens) else None

        # Delegate commands to functions
        if mainCmd is None:
            if controller.isPluginSelected():
                return self._cmdAgain([])
            else:
                return
        
        
        try:
            response = {
                'exit':     self._cmdExit,
                'help':     self._cmdHelp,
                'init':     self._cmdInit,
                'list':     self._cmdList,
                'select':   self._cmdSelect,
                'again':    self._cmdAgain,
                'set':      self._cmdSet,
                'next':     self._cmdNext,
            }.get(mainCmd, lambda x: self._cmdAnswer([mainCmd] + x))(tokens[1:])
        except ToneEduError, e:
            return e.message
        
        return response
    
    # Here starts the definition of command-handle functions
    @paramNum(1)
    def _cmdSelect(self, params):
        plugin = params[0]
        controller.selectPlugin(plugin)
        self._prompt_string = "%s> " % plugin 
        response = ["Plugin selected successfully"]
        response.append(controller.getQuestion())
        return joinLines(response)

    @paramNum(0)
    def _cmdList(self, params):
        response = []

        for name, desc in controller.getPlugins().items():
            response.append("\tName:\t\t%s" % name)
            response.append("\tDescription:\t%s" % desc)

        if not response:
            return "No plugins available"
        else:
            response.insert(0, "Available plugins:")

        return joinLines(response)

    @paramNum(0)
    def _cmdHelp(self, params):
        response = []
        response.append("Commands:")
        response.append("\tinit\t\tInitializes the MIDI device. Use: 'init [<device-number>]'")
        response.append("\tnext\t\tPrints the solution of the last question, and gives a new one")
        response.append("\tagain\t\tRepeats the last question")
        response.append("\thelp\t\tDisplays this information")
        response.append("\tlist\t\tDisplays the available plugins")
        response.append("\tselect\t\tSelect a plugin as active. Use: 'select <name-of-plugin>'")
        response.append("\tset\t\tSets plugin-related parameters (see the plugin help below). Use: 'set <key> <value>'")
        response.append("\texit\t\tExits from the program")
        if controller.isPluginSelected():
            response.append("")
            response.append("Plugin help:")
            response.append(controller.pluginHelp())
            response.append("")
            answerList = controller.answerList()
            optionList = controller.optionList()
            response.append("* List of possible answers (you can just type in them after the prompt):")
            response += ["\t%-40s%s" % (", ".join(short), ", ".join(long)) for short, long in answerList]
            response.append("")
            response.append("* List of possible settings (you can set them with 'set' command")
            response += ["\t%-16s%s" % (key, desc) for key, desc in optionList.items()]
        
        return joinLines(response)

    @paramNum(0)
    def _cmdAgain(self, params):
        return controller.tryAgain()
    
    @paramNum(0)
    def _cmdNext(self, params):
        response = [controller.getAnswer()]
        response.append(controller.getQuestion())
        
        return joinLines(response)

    @paramCheck(lambda pNum: pNum >= 2)
    def _cmdSet(self, params):
        return controller.setOption(params[0], " ".join(params[1:]))
    
    def _cmdAnswer(self, params):
        response = []

        if controller.checkAnswer(" ".join(params)):
            response.append("Right answer!")
            response.append(controller.getQuestion())
        else:
            response.append("Wrong answer!")
            response.append(controller.tryAgain())
        
        return joinLines(response)
        
    @paramCheck(lambda pNum: pNum <= 1)
    def _cmdInit(self, params):
        response = []
        if len(params) == 0:
            response.append("Available devices:")
            for index, name in controller.getSoundDevices().items():
                response.append("\t%s\t%s" % (str(index), name))
        else:
            try:
                devnum = int(params[0])
                controller.initSoundDevice(devnum)
                response.append("Device successfully initialized")
            except ValueError:
                return "The 'init' command device number parameter should be a number. Check the output of 'init' command without parameters"
            except NoSuchSoundDevice, e:
                return e.message

        return joinLines(response)

    @paramNum(0)
    def _cmdExit(self, params):
        controller.exit()

